home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
visulztn
/
saoimage
/
saoimage.lha
/
vms
/
zfiovi.c
< prev
Wrap
C/C++ Source or Header
|
1990-05-12
|
15KB
|
690 lines
/*
* ZFIOVI.C -- VMS Imtool driver. These interface routines are used by the
* interactive binary graphics interface (ZFIOGD) and any image display
* programs that support the Imtool protocol.
*
* Interface routines:
*
* zopnvi Open channel for VMS/IRAF-Imtool communication.
* zclsvi Close channel.
* zardvi Read from channel.
* zawrvi Write to channel.
* zawtvi Wait on read/write completion.
*
* Supplementary interface routines (not used by VMS/IRAF ZFIOGD):
*
* ZBufSize Returns maximum i/o transfer size.
* ZPending Check if data is waiting to be read.
* ZSelectAsyncInput Allow asynchronous i/o notification.
*
* NOTES
*
* For this prototype implementation, VMS mailboxes are used. A limit
* of 8192 bytes per i/o operation is assumed by ZFIOGD and any image
* display program that uses these routines (e.g., SAOimage). The i/o
* is effectively synchronous given the behavior of VMS mailboxes.
*
* The supplementary interface routines are to support image display
* programs which need to wait on window events and Imtool "pipe" i/o
* simultaneously a la Unix select(). These functions are modelled
* after similar ones in Xwindows (which may be VMS-dependent also).
*
* Since this will be linked into VMS/IRAF applications, the standard
* C library functions are not used. Only VMS/IRAF-supported ones
* and VMS system routines are referenced here.
*
* The mailbox device names are passed between the cooperating
* processes via locks. This somewhat sneaky approach is used
* so that GRPNAM privilege is not required for VMS/IRAF users.
* This approach assumes that mailbox device names (_MBAnnnn:)
* will fit into the 16-byte lock value block, which is not a
* problem as of VMS V5.2.
*
* HISTORY
*
* Oct-1989 Jay Travisano Space Telescope Science Institute
* Prototype implementation to support SAOimage with VMS/IRAF.
*/
#include <descrip.h> /* Descriptor definitions */
#include <dvidef.h> /* Get device info definitions */
#include <jpidef.h> /* Get job/process info definitions */
#include <lckdef.h> /* Lock definitions */
#include <iodef.h> /* I/O definitions */
#include <ssdef.h> /* System status definitions */
#include <syidef.h> /* Get system info definitions */
#ifdef IMTOOL
#define import_spp /* Import the IRAF SPP constants */
#include <iraf.h>
#else
#define READ_ONLY 1 /* file access modes */
#define XERR (-1)
#define XOK 0
#define XINT int
#define XLONG long
#endif
#define SZ_MBX 8192 /* maximum transfer size */
#define SZ_MBXNAME 48 /* size of logical mailbox name */
#define SZ_USERNAME 12 /* VMS size limits */
#define SZ_NODENAME 15
#define SZ_LOCKNAME 31
#define SZ_LOCKVALBLK 16
#define VMS_OKAY(s) (s & 1) /* VMS status code checking */
#define VMS_ERROR(s) (!(s & 1))
#define SET_DESCRIPTOR(name,value,length) \
name.dsc$a_pointer = value; \
name.dsc$w_length = length; \
name.dsc$b_dtype = DSC$K_DTYPE_T; \
name.dsc$b_class = DSC$K_CLASS_S
typedef struct dsc$descriptor_s DESC;
struct io_status_block {
short status;
short count;
long pid;
};
typedef struct io_status_block IOSB;
struct lock_status_block {
short status;
short reserved;
long lkid;
char valblk[SZ_LOCKVALBLK];
};
typedef struct lock_status_block LOCK;
struct channel_info {
short mbx_chan;
IOSB iosb;
int lockid;
int qio_status;
int io_pending;
int (*ast)();
int ast_arg;
};
typedef struct channel_info CHANNEL;
#define NUM_CHANNELS 2
static CHANNEL channels[NUM_CHANNELS];
/*
* ZOPNVI -- Open an IRAF-Imtool communication path using mailboxes.
*/
zopnvi (imtool_name, mode, chan)
char *imtool_name;
XINT *mode;
XINT *chan;
{
char username[SZ_USERNAME+1];
DESC _username;
char mbxname[SZ_MBXNAME+1];
DESC _mbxname;
char devname[SZ_LOCKVALBLK];
DESC _devname;
short len, ch;
int stat, lockid, i;
/* Generate mailbox logical name from Imtool name and username.
*/
SET_DESCRIPTOR (_username, username, sizeof(username)-1);
lib$getjpi (&JPI$_USERNAME, 0, 0, 0, &_username, &len);
username[len] = 0;
while (username[--len] == ' ') /* trim trailing blanks */
username[len] = 0;
strcat (strcat (strcpy (mbxname, imtool_name), "_"), username);
/* Open or create the mailbox. First, enqueue a lock using the
* mailbox logical name just formatted. If the mailbox already
* exists, the lock value returned will contain the device name.
* If not, we'll have to create the mailbox and update the lock
* with the actual device name. Dequeue the lock on any error.
*/
if ((lockid = Enqueue_Lock (mbxname, devname)) == 0) {
*chan = XERR;
return;
}
if (devname[0] && strncmp (devname, "_MBA", 4) == 0) {
/* Mailbox already exists; assign a channel to it.
*/
SET_DESCRIPTOR (_devname, devname, strlen(devname));
stat = sys$assign (&_devname, &ch, 0, 0);
if VMS_ERROR(stat) {
Dequeue_Lock (lockid);
putmsg (devname, 0);
putmsg ("ZFIOVI: Mailbox assign error", stat);
*chan = XERR;
return;
}
Update_Lock (lockid, 0);
} else {
/* Create the mailbox and update the lock with the
* actual device name.
*/
int bufquo;
if (*mode == READ_ONLY)
bufquo = SZ_MBX * 2;
else
bufquo = SZ_MBX;
SET_DESCRIPTOR (_mbxname, mbxname, strlen(mbxname));
stat = sys$crembx (0, &ch, SZ_MBX, bufquo, 0, 0, &_mbxname);
if VMS_ERROR(stat) {
Dequeue_Lock (lockid);
putmsg (mbxname, 0);
putmsg ("ZFIOVI: Mailbox creation error", stat);
*chan = XERR;
return;
}
SET_DESCRIPTOR (_devname, devname, sizeof(devname));
lib$getdvi (&DVI$_DEVNAM, &ch, 0, 0, &_devname, &len);
devname[len] = 0;
Update_Lock (lockid, devname);
}
#ifdef DEBUG
/* Print logical and device names of mailbox.
*/
{
char dbgmsg[80];
strcat (strcat (strcpy (dbgmsg, mbxname), " = "), devname);
putmsg (dbgmsg, 0);
}
#endif
/* Get a free channel structure and initialize.
*/
for (i = 0; i < NUM_CHANNELS; i++)
if (channels[i].mbx_chan == 0)
break;
if (i == NUM_CHANNELS) {
putmsg ("ZFIOVI: No channel structures available", 0);
sys$dassgn (ch);
Dequeue_Lock (lockid);
*chan = XERR;
} else {
CHANNEL *c;
c = &channels[i];
c->mbx_chan = ch;
c->lockid = lockid;
c->qio_status = 0;
c->ast = 0;
c->ast_arg = 0;
if (*mode == READ_ONLY) {
/* Check if there are messages waiting to be read.
* This number is in the two low-order bytes of the
* device-dependent longword.
*/
lib$getdvi (&DVI$_DEVDEPEND, &ch, 0, &c->io_pending);
c->io_pending &= 0x0000FFFF;
} else {
c->io_pending = 0;
}
*chan = c;
}
}
/*
* ZCLSVI -- Close IRAF-Imtool connection.
*/
zclsvi (chan, status)
XINT *chan;
XINT *status;
{
CHANNEL *c;
int stat;
/* Deassign the mailbox channel, dequeue the associated lock,
* and "free" the channel structure.
*/
c = *chan;
stat = sys$dassgn (c->mbx_chan);
if VMS_ERROR(stat) {
putmsg ("ZFIOVI: Error deassigning mailbox channel", stat);
*status = XERR;
} else {
*status = XOK;
}
Dequeue_Lock (c->lockid);
c->mbx_chan = 0;
}
/*
* ZARDVI -- Asynchronous read on IRAF-Imtool channel.
*/
zardvi (chan, buf, maxbytes, loffset)
XINT *chan;
char *buf;
XINT *maxbytes;
XLONG *loffset; /* ignored */
{
CHANNEL *c;
/* Post a read on the mailbox.
*/
c = *chan;
c->qio_status = sys$qio (0, c->mbx_chan, IO$_READVBLK,
&c->iosb, 0, 0,
buf, *maxbytes, 0,0,0,0);
}
/*
* ZAWRVI -- Asynchronous write to IRAF-Imtool channel.
*/
zawrvi (chan, buf, nbytes, loffset)
XINT *chan;
char *buf;
XINT *nbytes;
XLONG *loffset; /* ignored */
{
CHANNEL *c;
/* Write to the mailbox.
*/
c = *chan;
c->qio_status = sys$qio (0, c->mbx_chan, IO$_WRITEVBLK | IO$M_NOW,
&c->iosb, 0, 0,
buf, *nbytes, 0,0,0,0);
}
/*
* ZAWTVI -- Wait for asynchronous read/write to complete.
*/
zawtvi (chan, status)
XINT *chan;
XINT *status;
{
CHANNEL *c;
/* Wait on i/o to complete.
*/
c = *chan;
sys$synch (0, &c->iosb);
if VMS_ERROR(c->qio_status) {
putmsg ("ZFIOVI: QIO error", c->qio_status);
*status = XERR;
} else if (c->iosb.status == SS$_ENDOFFILE) {
putmsg ("ZFIOVI: Unexpected EOF on mailbox", c->iosb.status);
*status = 0;
} else if VMS_ERROR(c->iosb.status) {
putmsg ("ZFIOVI: Mailbox i/o error", c->iosb.status);
*status = XERR;
} else {
*status = c->iosb.count;
if (c->io_pending)
c->io_pending--;
}
}
/*
* ZBufSize -- Return maximum i/o transfer size, in bytes.
*/
ZBufSize (CHANNEL *c)
{
return SZ_MBX;
}
/*
* ZPending -- Return data pending count.
*/
ZPending (CHANNEL *c)
{
return c->io_pending;
}
/*
* ZSelectAsyncInput -- Set up for asynchronous notification when i/o is
* ready on a given channel. This must be reset by the application
* _after_ it has read the data from the mailbox.
*/
ZSelectAsyncInput (CHANNEL *c, int (*ast)(), int ast_arg)
{
if (ast)
Set_Attn_AST (c);
c->ast = ast;
c->ast_arg = ast_arg;
}
/*
* Set_Attn_AST -- Set a write attention AST on the mailbox channel
*/
static
Set_Attn_AST (CHANNEL *c)
{
int Attn_AST();
int stat;
stat = sys$qiow (0, c->mbx_chan, IO$_SETMODE | IO$M_WRTATTN,
&c->iosb, 0, 0,
Attn_AST, c, 0,0,0,0);
if VMS_ERROR(stat)
putmsg ("ZFIOVI: Error setting write attn ast", stat);
else if VMS_ERROR(c->iosb.status)
putmsg ("ZFIOVI: Error setting write attn ast", c->iosb.status);
}
/*
* Attn_AST -- Write attention AST routine. Increment the i/o pending
* count and invoke the application's AST routine.
*/
static
Attn_AST (CHANNEL *c)
{
c->io_pending++;
if (c->ast)
(* c->ast) (c->ast_arg);
}
/*
* PUTMSG - Put an error message and/or an associated system error
* string to SYS$OUTPUT.
*/
static
putmsg (char *msg, int code)
{
DESC _msg;
struct {
short arg_count;
short msg_options;
int msg_code;
} msgvec;
if (msg) {
SET_DESCRIPTOR (_msg, msg, strlen(msg));
lib$put_output (&_msg);
}
if (code) {
msgvec.arg_count = 1;
msgvec.msg_options = 0x0F; /* output full message */
msgvec.msg_code = code;
sys$putmsg (&msgvec);
}
}
/*
* ENQUEUE_LOCK -- Enqueue an exclusive lock. Copies the value from the
* lock status block. The function return value is the lock id for
* subsequent lock operations, or zero if there was an error.
*
* Locks are cluster-wide by default, so the local node name is added
* to the specified lock name. This will prevent conflicts in cases
* where shared accounts are used in a VAX cluster environment.
*/
static
int Enqueue_Lock (char *lockname, char *valblk)
{
int stat, sys$enqw();
short len;
LOCK lblock;
char nodename[SZ_NODENAME+1];
DESC _nodename;
char resnam[SZ_LOCKNAME*2];
DESC _resnam;
/* Generate the lock resource name of the form: lockname@nodename.
* This string is truncated if necessary.
*/
SET_DESCRIPTOR (_nodename, nodename, sizeof(nodename)-1);
lib$getsyi (&SYI$_NODENAME, 0, &_nodename, &len, 0, 0);
nodename[len] = 0;
strcat (strcat (strcpy (resnam, lockname), "@"), nodename);
resnam[SZ_LOCKNAME] = 0;
SET_DESCRIPTOR (_resnam, resnam, strlen(resnam));
/* Enqueue the lock.
*/
stat = sys$enqw (
0, /* event flag */
LCK$K_EXMODE, /* lock mode */
&lblock, /* lock status block */
LCK$M_VALBLK, /* flags (get value block) */
&_resnam, /* resource name */
0, /* parent id */
0, /* AST address */
0, /* AST parameter */
0, /* Blocking AST */
0, /* access mode */
0 /* null argument */
);
if VMS_ERROR(stat) {
putmsg ("ZFIOVI: Error enqueueing lock request", stat);
return 0;
}
if VMS_ERROR(lblock.status) {
putmsg ("ZFIOVI: Lock error from enqueue request", lblock.status);
return 0;
}
if (valblk)
lib$movc3 (&SZ_LOCKVALBLK, lblock.valblk, valblk);
return lblock.lkid;
}
/*
* UPDATE_LOCK -- Update a lock value block by converting it to null mode.
* This keeps the lock and its value block around for other processes
* to access later.
*/
static
Update_Lock (int lockid, char *valblk)
{
int stat, sys$enqw();
int flags;
LOCK lblock;
lblock.lkid = lockid;
flags = LCK$M_CONVERT;
if (valblk) {
flags |= LCK$M_VALBLK;
lib$movc3 (&SZ_LOCKVALBLK, valblk, lblock.valblk);
}
/* Enqueue the lock (conversion).
*/
stat = sys$enqw (
0, /* event flag */
LCK$K_NLMODE, /* lock mode */
&lblock, /* lock status block */
flags, /* flags */
0, /* resource name */
0, /* parent id */
0, /* AST address */
0, /* AST parameter */
0, /* Blocking AST */
0, /* access mode */
0 /* null argument */
);
if VMS_ERROR(stat)
putmsg ("ZFIOVI: Error enqueueing lock conversion request", stat);
if VMS_ERROR(lblock.status)
putmsg ("ZFIOVI: Lock error from conversion request", lblock.status);
}
/*
* DEQUEUE_LOCK - Dequeue a lock.
*/
static
Dequeue_Lock (int lockid)
{
int stat, sys$deq();
stat = sys$deq (
lockid, /* lock identification */
0, /* value block */
0, /* access mode */
0 /* $DEQ options */
);
if VMS_ERROR(stat)
putmsg ("ZFIOVI: Error dequeueing lock", stat);
}
#ifdef REQUIRES_GRPNAM_PRIVILEGE
/**
** This is here for reference only. It's use has been superseded
** by the use of locks above. If GRPNAM privilege is assumed,
** this function is called before a call to SYS$CREMBX, whether
** or not the mailbox has already been created.
**/
#include <lnmdef.h> /* Logical name definitions */
#include <prvdef.h> /* Privilege mask definitions */
/*
* SET_GROUP_TABLE -- Set logical so that mailbox logical name is put
* into the group table. Note that we cannot use LIB$SET_LOGICAL here
* (the easier approach) since this software will be run as part of a
* VMS/IRAF subprocess which has no CLI associated with it. If the
* process does not currently have GRPNAM privilege, issue a warning.
* This will be more useful than getting a mailbox creation error.
*/
static
set_group_table ( )
{
$DESCRIPTOR (_name, "LNM$TEMPORARY_MAILBOX");
$DESCRIPTOR (_value, "LNM$GROUP");
$DESCRIPTOR (_table, "LNM$PROCESS_DIRECTORY");
struct item_list {
short len; /* length of buffer */
short code; /* service-dependent option code */
char *buf; /* user buffer */
short *blen; /* return length for data in buf */
} itmlst[2];
int stat, curpriv;
char fallback_msg[] =
"Cannot set GROUP table for mailbox name; using JOB table";
lib$getjpi (&JPI$_CURPRIV, 0, 0, &curpriv, 0, 0);
if (curpriv & PRV$M_GRPNAM) {
itmlst[0].len = _value.dsc$w_length;
itmlst[0].code = LNM$_STRING;
itmlst[0].buf = _value.dsc$a_pointer;
itmlst[0].blen = 0;
itmlst[1].len = 0;
itmlst[1].code = 0;
stat = sys$crelnm (&LNM$M_NO_ALIAS, &_table, &_name, 0, itmlst);
if VMS_ERROR(stat)
putmsg (fallback_msg, stat);
} else {
putmsg (fallback_msg, 0);
}
}
#endif